#region

using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

#endregion

namespace GGEditor.API.Utility.Forms
{
    public class RestorableForm : Form, INotifyPropertyChanged
    {
        #region Events

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        #endregion

        #region Public Properties

        private bool fullscreen;
        private RestorableFormState restorableFormState;

        [Browsable(false)]
        [SettingsBindable(true)]
        public RestorableFormState RestorableFormState
        {
            get { return restorableFormState; }
            set
            {
                restorableFormState = value;
                PropertyChanged(this, new PropertyChangedEventArgs("RestorableFormState"));
            }
        }

        public bool Fullscreen
        {
            get { return fullscreen; }
            set
            {
                if (value == fullscreen)
                    return;

                if (value) {
                    preFullscreenBounds = NormalBounds;
                    preFullscreenBorderStyle = FormBorderStyle;
                    NonFullscreenWindowState = WindowState;

                    WindowState = FormWindowState.Maximized;
                    FormBorderStyle = FormBorderStyle.None;

                    Screen screen = Screen.FromControl(this);
                    SetWindowPos(
                        Handle, HWND_TOP, screen.Bounds.Left, screen.Bounds.Top, screen.Bounds.Width,
                        screen.Bounds.Height, SWP_SHOWWINDOW);
                } else {
                    WindowState = NonFullscreenWindowState;
                    FormBorderStyle = preFullscreenBorderStyle;
                    Bounds = NormalBounds;
                }

                // Need to do this at the end to preserve the meaning
                // of NormalBounds and NonFullscreenWindowState:
                fullscreen = value;
            }
        }

        #endregion

        #region Private Properties

        private FormWindowState nonFullscreenWindowState;

        private FormBorderStyle preFullscreenBorderStyle;
        private Rectangle preFullscreenBounds;

        private FormWindowState NonFullscreenWindowState
        {
            get
            {
                if (Fullscreen)
                    return nonFullscreenWindowState;
                return WindowState;
            }
            set { nonFullscreenWindowState = value; }
        }

        private Rectangle NormalBounds
        {
            get
            {
                if (Fullscreen)
                    return preFullscreenBounds;

                if (WindowState == FormWindowState.Normal)
                    return Bounds;

                return RestoreBounds;
            }
        }

        #endregion

        #region Event Handlers

        protected override void OnClosed(EventArgs e)
        {
            SaveWindowState();
            base.OnClosed(e);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            RestoreWindowState();
        }

        #endregion

        #region Methods

        private void SaveWindowState()
        {
            RestorableFormState state = new RestorableFormState();
            state.Bounds = NormalBounds;
            state.WindowState = NonFullscreenWindowState;
            state.Fullscreen = Fullscreen;
            RestorableFormState = state;
        }

        private void RestoreWindowState()
        {
            if (RestorableFormState != null) {
                Bounds = ConstrainToScreen(RestorableFormState.Bounds);
                WindowState = RestorableFormState.WindowState;
                Fullscreen = RestorableFormState.Fullscreen;
            }
        }

        private Rectangle ConstrainToScreen(Rectangle bounds)
        {
            Screen screen = Screen.FromRectangle(RestorableFormState.Bounds);
            Rectangle workingArea = screen.WorkingArea;

            int width = Math.Min(bounds.Width, workingArea.Width);
            int height = Math.Min(bounds.Height, workingArea.Height);
            int left = Math.Min(workingArea.Right - width, Math.Max(bounds.Left, workingArea.Left));
            int top = Math.Min(workingArea.Bottom - height, Math.Max(bounds.Top, workingArea.Top));

            return new Rectangle(left, top, width, height);
        }

        #region Win32 Fullscreen API

        private const int SWP_SHOWWINDOW = 64;
        private static IntPtr HWND_TOP = IntPtr.Zero;

        [DllImport("user32.dll")]
        private static extern void SetWindowPos(
            IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);

        #endregion

        #endregion
    }
}